package com.yang.designmode.singleton;

import java.io.*;

/**
 * 双重检查锁定（Double-Check Locking）实现的单例模式。
 * 保证了多线程环境下的单例实例创建，并且只进行一次同步操作，提高性能。
 */
public class DoubleCheckSingletonSerializable implements Serializable {

    /**
     * 使用 volatile 关键字确保多线程环境下的可见性和禁止指令重排序。
     * 保证在所有线程中该变量的值是一致的。
     */
    private static volatile DoubleCheckSingletonSerializable instance;

    /**
     * 私有构造函数，防止外部通过 new 创建实例。
     */
    private DoubleCheckSingletonSerializable(){
        if(instance!=null){
            //说明有人想要破坏单例特性，创建多个实例
            throw new RuntimeException(DoubleCheckSingletonSerializable.class+"单例模式不允许创建多个实例");
        }
    }

    /**
     * 获取单例对象的方法。
     * 使用双重检查锁定机制来延迟初始化单例对象。
     *
     * @return 单例对象
     */
    public static DoubleCheckSingletonSerializable getInstance(){
        // 第一次检查：如果 instance 已经被初始化，则直接返回，避免不必要的同步开销
        if(instance == null){
            // 同步类锁，保证只有一个线程进入代码块
            synchronized (DoubleCheckSingletonSerializable.class){
                // 第二次检查：确保只有一个实例被创建
                if(instance == null){
                    // 创建单例对象
                    //潜在的安全问题 线程安全：原子性、可见性、顺序性
                    /*
                      1. 分配内存空间
                      2. 初始化对象
                      3. 将 instance 指向分配的内存地址
                     */
                    instance = new DoubleCheckSingletonSerializable();
                }
            }
        }
        return instance;
    }

    /**
     * 重写readResolve方法 在反序列化时候调用，不会创建新的对象，返回该方法的对象
     * @return
     * @throws ObjectStreamException
     */
    public Object readResolve() throws ObjectStreamException {
        return getInstance();
    }

    public static void main(String[] args) throws Exception {
        // 正常创建单例对象
        DoubleCheckSingletonSerializable instance_1 = DoubleCheckSingletonSerializable.getInstance();
        System.out.println(instance_1);

        //通过序列化和反序列化方式破坏单例
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./c.txt"));
        oos.writeObject(instance_1);
        oos.flush();
        oos.close();
        //通过反序列化方式读取
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./c.txt"));
        DoubleCheckSingletonSerializable instance_2 = (DoubleCheckSingletonSerializable) ois.readObject();
        ois.close();

        System.out.println("instance_1 = "+instance_1);
        System.out.println("instance_2 = "+instance_2);
    }
}
